package com.tpvlog.dfs.datanode.server;

import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;

public class NetworkRequest {
    // 文件上传
    public static final Integer REQUEST_SEND_FILE = 1;
    // 文件下载
    public static final Integer REQUEST_READ_FILE = 2;

    // Processor标识
    private Integer processorId;

    // 该请求是哪个客户端发送过来的
    private String client;

    // 本次网络请求对应的SelectionKey
    private SelectionKey key;

    // 本次网络请求对应的Channel
    private SocketChannel channel;

    // 缓存的数据，处理拆包
    private InflightRequest cachedRequest = new InflightRequest();
    private ByteBuffer cachedRequestTypeBuffer;
    private ByteBuffer cachedFilenameLengthBuffer;
    private ByteBuffer cachedFilenameBuffer;
    private ByteBuffer cachedFileLengthBuffer;
    private ByteBuffer cachedFileBuffer;

    /**
     * 读取字节流
     */
    public void read() {
        try {
            Integer requestType = null;
            if (cachedRequest.requestType != null) {
                requestType = cachedRequest.requestType;
            } else {
                requestType = getRequestType(channel);
            }
            if (requestType == null) {
                return;
            }
            System.out.println("从请求中解析出来请求类型：" + requestType);

            if (REQUEST_SEND_FILE.equals(requestType)) {
                handleSendFileRequest(channel, key);
            } else if (REQUEST_READ_FILE.equals(requestType)) {
                handleReadFileRequest(channel, key);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取本次请求的类型
     */
    public Integer getRequestType(SocketChannel channel) throws Exception {
        Integer requestType = null;

        if (cachedRequest.requestType != null) {
            return cachedRequest.requestType;
        }

        ByteBuffer requestTypeBuffer = null;
        if (cachedRequestTypeBuffer != null) {
            requestTypeBuffer = cachedRequestTypeBuffer;
        } else {
            requestTypeBuffer = ByteBuffer.allocate(4);
        }

        channel.read(requestTypeBuffer);
        if (!requestTypeBuffer.hasRemaining()) {
            // 已经读取出来了4个字节，可以提取出来requestType了
            // 将position变为0，limit还是维持着4
            requestTypeBuffer.rewind();
            requestType = requestTypeBuffer.getInt();
            cachedRequest.requestType = requestType;
        } else {
            cachedRequestTypeBuffer = requestTypeBuffer;
        }
        return requestType;
    }

    /**
     * 发送文件
     */
    private void handleSendFileRequest(SocketChannel channel, SelectionKey key) throws Exception {
        // 从请求中解析文件名
        String filename = getFilename(channel);
        System.out.println("从网络请求中解析出来文件名：" + filename);
        if (filename == null) {
            return;
        }
        // 从请求中解析文件大小
        Long fileLength = getFileLength(channel);
        System.out.println("从网络请求中解析出来文件大小：" + fileLength);
        if (fileLength == null) {
            return;
        }

        // 循环不断的从channel里读取数据，并写入磁盘文件
        ByteBuffer fileBuffer = null;
        if (cachedFileBuffer != null) {
            fileBuffer = cachedFileBuffer;
        } else {
            fileBuffer = ByteBuffer.allocate(Integer.valueOf(String.valueOf(fileLength)));
        }

        channel.read(fileBuffer);
        if (!fileBuffer.hasRemaining()) {
            fileBuffer.rewind();
            cachedRequest.fileContent = fileBuffer;
            cachedRequest.hasCompletedRead = true;
            System.out.println("本次文件上传请求读取完毕.......");
        } else {
            cachedFileBuffer = fileBuffer;
            System.out.println("本次文件上传出现拆包问题，缓存起来，下次继续读取.......");
        }
    }

    /**
     * 获取文件名
     */
    private String getFilename(SocketChannel channel) throws Exception {
        String filename = null;
        if (cachedRequest.filename != null) {
            return cachedRequest.filename;
        } else {
            Integer filenameLength = null;
            // 读取文件名的大小
            if(cachedRequest.filenameLength == null) {
                ByteBuffer filenameLengthBuffer = null;
                if(cachedFilenameLengthBuffer != null) {
                    filenameLengthBuffer = cachedFilenameLengthBuffer;
                } else {
                    filenameLengthBuffer = ByteBuffer.allocate(4);
                }

                channel.read(filenameLengthBuffer);

                if(!filenameLengthBuffer.hasRemaining()) {
                    filenameLengthBuffer.rewind();
                    filenameLength = filenameLengthBuffer.getInt();
                    cachedRequest.filenameLength = filenameLength;
                } else {
                    cachedFilenameLengthBuffer = filenameLengthBuffer;
                    return null;
                }
            }

            // 读取文件名
            ByteBuffer filenameBuffer = null;
            if(cachedFilenameBuffer != null) {
                filenameBuffer = cachedFilenameBuffer;
            } else {
                filenameBuffer = ByteBuffer.allocate(filenameLength);
            }

            channel.read(filenameBuffer);
            if(!filenameBuffer.hasRemaining()) {
                filenameBuffer.rewind();
                filename = new String(filenameBuffer.array());
            } else {
                cachedFilenameBuffer = filenameBuffer;
            }
            cachedRequest.filename = filename;
        }
        return filename;
    }

    /**
     * 获取文件大小
     */
    private Long getFileLength(SocketChannel channel) {
        Long fileLength = null;

        if(cachedRequest.filesize != null) {
            return cachedRequest.filesize;
        } else {
            ByteBuffer filesizeBuffer = null;
            if(cachedFileLengthBuffer != null) {
                filesizeBuffer = cachedFileLengthBuffer;
            } else {
                filesizeBuffer = ByteBuffer.allocate(8);
            }


            if(!filesizeBuffer.hasRemaining()) {
                filesizeBuffer.rewind();
                fileLength = filesizeBuffer.getLong();
                cachedRequest.filesize = fileLength;
            } else {
                cachedFileLengthBuffer = filesizeBuffer;
            }
        }
        return fileLength;
    }


    /**
     * 读取文件
     */
    private void handleReadFileRequest(SocketChannel channel, SelectionKey key) throws Exception {
        // 从请求中解析文件名
        String filename = getFilename(channel);
        System.out.println("从网络请求中解析出来文件名：" + filename);
        if(filename == null) {
            return;
        }
        cachedRequest.hasCompletedRead = true;
    }

    /**
     * 本次请求是否已经读取完成
     */
    public boolean hasCompletedRead() {
        Long hasReaded = cachedRequest.hasReadedSize;
        Long total = cachedRequest.filesize;
        if (hasReaded == null) {
            return false;
        }
        if (total == null) {
            return false;
        }
        return hasReaded.equals(total);
    }


    /**
     * 缓存数据
     */
    class InflightRequest {
        // 请求类型
        Integer requestType;
        // 文件名，以前缀分隔符开始，比如/dir/enclosure/qq.jpg
        String filename;
        // 文件名大小
        Integer filenameLength;
        // 文件总大小
        Long filesize;
        // 文件内容
        ByteBuffer fileContent;
        // 已读取的大小
        Long hasReadedSize;
        // 是否读取完整
        Boolean hasCompletedRead = false;

        public InflightRequest(String filename, Long imageSize, Long hasReadedSize, Integer requestType) {
            this.filename = filename;
            this.filesize = imageSize;
            this.hasReadedSize = hasReadedSize;
            this.requestType = requestType;
        }

        public InflightRequest() {
        }

        @Override
        public String toString() {
            return "InflightRequest{" +
                    "filename='" + filename + '\'' +
                    ", filesize=" + filesize +
                    ", hasReadedSize=" + hasReadedSize +
                    '}';
        }
    }

    public Integer getRequestType() {
        return cachedRequest.requestType;
    }

    public String getFilename() {
        return cachedRequest.filename;
    }

    public Integer getFilenameLength() {
        return cachedRequest.filenameLength;
    }

    public Long getFilesize() {
        return cachedRequest.filesize;
    }

    public ByteBuffer getFileContent() {
        return cachedRequest.fileContent;
    }

    public Long getHasReadedSize() {
        return cachedRequest.hasReadedSize;
    }

    public Boolean getHasCompletedRead() {
        return cachedRequest.hasCompletedRead;
    }

    public Integer getProcessorId() {
        return processorId;
    }

    public void setProcessorId(Integer processorId) {
        this.processorId = processorId;
    }

    public String getClient() {
        return client;
    }

    public void setClient(String client) {
        this.client = client;
    }

    public SelectionKey getKey() {
        return key;
    }

    public void setKey(SelectionKey key) {
        this.key = key;
    }

    public SocketChannel getChannel() {
        return channel;
    }

    public void setChannel(SocketChannel channel) {
        this.channel = channel;
    }
}
